package butines.game2.pathfinder;

import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import butines.common.math.Vector2D;
import butines.game2.Drawer;
import butines.game2.entity.Building;

public class Map {

    private Drawer drawer = Drawer.getInstance();

    private int numCellsX;
    private int numCellsY;
    private int numCells;
    private int cellSize;

    private Vector2D[] positionGrid;
    private boolean[] passableGrid;

    public Map(int cellSize, int numCellsX, int numCellsY) {
        this.cellSize = cellSize;
        this.numCellsX = numCellsX;
        this.numCellsY = numCellsY;
        this.numCells = numCellsX * numCellsY;
        create();
    }

    public int getNumCells() {
        return numCells;
    }

    public int getNumCellsX() {
        return numCellsX;
    }

    public int getNumCellsY() {
        return numCellsY;
    }

    public boolean isValidCell(int x, int y) {
        return (x >= 0 && x < numCellsX && y >= 0 && y < numCellsY);
    }

    private void create() {

        positionGrid = new Vector2D[numCells];
        passableGrid = new boolean[numCells];

        // need some temporaries to help calculate each node center
        float midX = cellSize / 2;
        float midY = cellSize / 2;

        // first create all the nodes
        for (int y = 0; y < numCellsY; y++) {
            for (int x = 0; x < numCellsX; x++) {
                positionGrid[y * numCellsX + x] = new Vector2D(midX + (x * cellSize), midY + (y * cellSize));
                passableGrid[y * numCellsX + x] = true;
            }
        }

    }

    public boolean isValidLocation(int current, int neighbor, boolean avoidCorners) {
        int sx = current % numCellsX;
        int sy = current / numCellsX;

        int x = neighbor % numCellsX;
        int y = neighbor / numCellsX;

        return isValidLocation(sx, sy, x, y, avoidCorners);
    }

    public boolean isValidLocation(int sx, int sy, int x, int y, boolean avoidCorners) {
        boolean valid = isValidCell(x, y) && isPassableGrid(x, y);

        if (valid && avoidCorners) {
            if (x - sx != 0 && y - sy != 0) {
                valid = isPassableGrid(x, sy) && isPassableGrid(sx, y);
            }
        }

        return valid;
    }

    public boolean isPassableGrid(int x, int y) {
        return passableGrid[y * numCellsX + x];
    }

    public void setPassableGrid(int x, int y, boolean passable) {
        passableGrid[y * numCellsX + x] = passable;
    }

    public Vector2D getPosition(int x, int y) {
        return positionGrid[y * numCellsX + x];
    }

    public Vector2D getPosition(int i) {
        return positionGrid[i];
    }

    public List<Integer> getNeighbours(int i) {
        int x = i % numCellsX;
        int y = i / numCellsX;
        return getNeighbours(x, y);
    }

    public List<Integer> getNeighbours(int x, int y) {
        List<Integer> neighbours = new ArrayList<Integer>();
        for (int i = -1; i < 2; i++) {
            for (int j = -1; j < 2; j++) {

                int cx = x + i;
                int cy = y + j;

                // skip if equal to this node
                if ((i == 0) && (j == 0)) {
                    continue;
                }

                if (!isValidCell(cx, cy) || !isPassableGrid(cx, cy)) {
                    continue;
                }

                neighbours.add(cy * numCellsX + cx);

            }
        }

        return neighbours;
    }

    public void debug(Graphics2D g2d, boolean drawNodeIDs, boolean drawEdges) {

        for (int i = 0; i < numCells; i++) {

            if (!passableGrid[i]) {
                int x = (i % numCellsX) * cellSize;
                int y = (i / numCellsX) * cellSize;
                drawer.color(255, 0, 0, 48);
                drawer.fill();
                drawer.rect(x, y, cellSize, cellSize);
            }

            drawer.color(200, 200, 200, 255);
            drawer.fill();
            drawer.circle(positionGrid[i].x, positionGrid[i].y, 3);

            if (drawNodeIDs) {
                drawer.center();
                drawer.print(positionGrid[i].x, positionGrid[i].y - 3, "%d", i);
            }

            if (drawEdges) {
                if (!passableGrid[i]) {
                    continue;
                }

                for (int j : getNeighbours(i)) {
                    drawer.line(positionGrid[i].x, positionGrid[i].y, positionGrid[j].x, positionGrid[j].y);
                }
            }

        }

    }

    public void unobstructedPassableGrid() {
        Arrays.fill(passableGrid, true);
    }

    public void obstructedPassableGrid(Building building) {
        int[][] grid = building.getPassableGrid();

        for (int y = 0; y < grid.length; y++) {
            for (int x = 0; x < grid[y].length; x++) {
                int x2 = building.getGridX() + x;
                int y2 = building.getGridY() + y;
                if (grid[y][x] == 1 && isValidCell(x2, y2)) {
                    setPassableGrid(x2, y2, false);
                }
            }
        }
    }

    /*
     * public boolean hasObstacle(int sx, int sy, int gx, int gy, boolean avoidCorners) { final class Vec { int x, y;
     * 
     * public Vec(int x, int y) { this.x = x; this.y = y; }
     * 
     * }
     * 
     * Vec v1 = new Vec(sx, sy); Vec v2 = new Vec(gx, gy);
     * 
     * if (v1.y > v2.y) { Vec tmp = v2; v2 = v1; v1 = tmp; }
     * 
     * float dx = v1.x - v2.x; float dy = v1.y - v2.y;
     * 
     * int lx = v1.x; int ly = v1.y;
     * 
     * for (int y = v1.y + 1; y <= v2.y; y++) { int x = (int) Math.ceil(v1.x + (y - v1.y) * dx/dy); if (!isValidLocation(lx, ly, x, y, avoidCorners)) { return true; } lx = x; ly =
     * y; }
     * 
     * return false; }
     */

}
